package org.wheel.plugins.toolkit.sql.helper;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Getter;
import org.wheel.plugins.toolkit.sql.core.GlobalConfig;
import org.wheel.plugins.toolkit.sql.enums.SqlModel;
import org.wheel.plugins.toolkit.sql.render.ParamRenderStrategy;
import org.wheel.plugins.toolkit.util.NullableConcurrentLinkedHashMap;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 全局参数生命周期 – 主查询 + 嵌套子查询零冲突
 */
public final class ParamBank implements ParamRenderStrategy {

    private final ParamBank parent;           // 父 bank（主查询或外层子查询）
    private final Map<String, Object> map =  new NullableConcurrentLinkedHashMap<>();
    private final AtomicInteger seq;          // 序号起始值
    private final SqlModel model;             // 你的 SqlModel 枚举

    @Getter
    private static final String prefix = "p";

    private final ThreadLocal<Deque<ParamBank>> STACK = ThreadLocal.withInitial(ArrayDeque::new);


    /** 返回当前线程顶层分支（仅供外层调用） */
    public ParamBank getOuterBranch() {
        Deque<ParamBank> dq = STACK.get();
        if (dq.size() < 2)  return current();   // 只有一层时退而求其次
        return dq.pollFirst();                  // 栈底即最外层
    }

    /** 真正新建一个**独立**子分支，并返回该分支引用 */
    public ParamBank openChildBranch() {
        ParamBank child = new ParamBank(current()); // 父=当前
        STACK.get().push(child);
        return child;
    }

    /** 子分支结束：合并到父分支并弹出自己 */
    public void closeChildBranch() {
        Deque<ParamBank> dq = STACK.get();
        if (dq.size() < 2) return;
        ParamBank child = dq.pop();
        ParamBank parent = dq.peek();
        if (parent != null) parent.absorb(child);
    }
    /** 打开新分支（主查询 or 子查询） */
    public ParamBank openBranch() {
        Deque<ParamBank> dq = STACK.get();
        ParamBank parent = dq.peek();
        ParamBank child = new ParamBank(parent);
        dq.push(child);
        return child;
    }

    /* ---------- 获取当前分支 ---------- */
    public ParamBank current() {
        Deque<ParamBank> dq = STACK.get();
        if (dq.isEmpty()) {
            // 兜底：自动打开根分支，保证不 NPE
            return openBranch();
        }
        return dq.peek();
    }

    /** 渲染前关闭分支，得到根节点 */
    public ParamBank root() {
        Deque<ParamBank> dq = STACK.get();
        while (dq.size() > 1) dq.pop();   // 关闭所有子查询
        ParamBank last = dq.peek();
        last.map.clear();
        return last;
    }

    /* ========== 生命周期构造 ========== */
    public ParamBank() {
        this(null, SqlModel.getDefaultModel());
    }
    public ParamBank(ParamBank parent) {
        this(parent, (parent ==null||parent.model==null)? GlobalConfig.getSqlModel(): parent.model);
    }
    private ParamBank(ParamBank parent, SqlModel model) {
        this.parent = parent;
        this.model = model;
        this.seq = new AtomicInteger(parent == null ? 0 : parent.seq.get());
    }

    /* ========== 收集入口 ========== */
//    public String addParam(Object value) {
//        String key = "param_" + seq.getAndIncrement();
//        map.put(key, value);
//        return model.render(key); // 返回你的占位符格式
//    }
    /* 原子取号 + 自动落库  一步完成 */
    public String nextParam(Object value) {
        String key = "p" + seq.getAndIncrement();
        map.put(key, value);
        return key;
    }
    /**
     * 把另一个 bank 的所有参数吸进来（用于子查询合并）
     */
    public void absorb(ParamBank child) {
        if (child == null || child == this) {
            return;
        }
        this.map.putAll(child.map);
        // seq 不需要回退，继续递增即可
    }
    /* ---------- 递归合并所有层级参数 ---------- */
    public LinkedHashMap<String, Object> toMap() {
        LinkedHashMap<String, Object> merged = new LinkedHashMap<>();
        if (parent != null) {
            merged.putAll(parent.toMap());
        }
        merged.putAll(map);
        return merged;
    }

    /**
     * 写入参数
     * 仅框架内部使用，用于把指定 key 写入当前分支。
     * 外部禁止调用，避免破坏序号。
     * 内部调用的时候，调用者需自行保证 key 不冲突。
     */
    @Deprecated   // 编译时提醒
    public void put(String key, Object value) {
        map.put(key, value);
    }

    /**
     * 判断参数是否存在
     * @param key 参数 key
     * @return 存在则返回 true
     */
    public boolean containsKey(String key) {
        return map.containsKey(key);
    }

    /* ========== 渲染策略 ========== */
    @Override
    public String render(String sql) {
        // 先渲染父
        if (parent != null) {
            parent.render(sql);
        }
        return StrUtil.replace(sql, ":(\\w+)", m -> {
            String key = m.group(1);
            Object val = map.get(key);
            return val == null ? m.group(0) : model.render(key);
        });
    }

    @Override
    public Object getParamObject() {
        Map<String, Object> merged = MapUtil.newHashMap();
        if (parent != null) {
            // 强制转型为 Map<String, Object>
            @SuppressWarnings("unchecked")
            Map<String, Object> parentMap = (Map<String, Object>) parent.getParamObject();
            merged.putAll(parentMap);
        }
        merged.putAll(map);
        return merged;
    }

    @Override
    public Map<String, Object> getParamMap() {
        return (Map<String, Object>) getParamObject();
    }
}